#include "PrecompiledHeader.h"
#include "CScene.h"

/*--------------------------------------------------------------------------*/
CScene* g_Scene = NULL;
/*--------------------------------------------------------------------------*/

enum CSceneCS
{
    ObjectsVector = 0
};

/*--------------------------------------------------------------------------*/

CScene::CScene() :
	IEngMod("CScene"),
	ICriticalSection(1),
	IKeyboardDriver(100, false),
	m_freeCamera(NULL)
{
	AssertModule(g_Shell);
	g_Shell->RegisterLuaFunction("setfcampos", sh::setfcampos);
	g_Shell->RegisterLuaFunction("setfcamrot", sh::setfcamrot);
	g_Shell->RegisterLuaFunction("getfcampos", sh::getfcampos);
	g_Shell->RegisterLuaFunction("fcamdebug", sh::fcamdebug);

	this->var_fcamera_enabled = g_Shell->AddVar("fcamera_enabled", 1);
	this->var_fcamera_movement_speed = g_Shell->AddVar("fcamera_movement_speed", 100.0f);
	this->var_fcamera_rotation_speed = g_Shell->AddVar("fcamera_rotation_speed", 1.0f);

	this->StartTask(0, CalcDesc(true, ECalc::MultiThreadHighPriority));
}

/*--------------------------------------------------------------------------*/

CScene::~CScene()
{
	AssertLogC(this->m_sceneInstances.empty() == false, "Some scenes left undeleted.");
}

/*--------------------------------------------------------------------------*/

void CScene::OnCalc(uint arg)
{
	g_Keyboard->AddKeyboardDriver(this);
	this->UpdateFreeCamera();

	for (auto iter = this->m_sceneInstances.begin(); iter != this->m_sceneInstances.end(); ++iter)
	{
		AssertLogC(iter->m_sceneInstance == NULL, "NULL in sceneInstances vector");

		if (iter->m_flags & ESceneInstanceContainerFlag::Ignore)
		{
			continue;
		}

		if (iter->m_sceneInstance->IsStarted())
		{
			iter->m_sceneInstance->AddTaskToCurrentQueue(0);
		}
	}
}

/*--------------------------------------------------------------------------*/

SceneInstance* CScene::CreateScene()
{
	SceneInstanceContainer sic;
	sic.m_sceneInstance = new SceneInstance();
	this->m_sceneInstances.push_back(sic);

	return sic.m_sceneInstance;
}

/*--------------------------------------------------------------------------*/

void CScene::DeleteScene(SceneInstance* scene)
{
	for (auto iter = this->m_sceneInstances.begin(); iter != this->m_sceneInstances.end();)
	{
		if (iter->m_sceneInstance == scene)
		{
			delete scene;
			iter = this->m_sceneInstances.erase(iter);
		}
		else
		{
			++iter;
		}
	}
}

/*--------------------------------------------------------------------------*/

void CScene::SetSceneFlags(SceneInstance* scene, uint flags)
{
	SceneInstanceContainer& sic = GetSceneInstanceContainer(scene);
	sic.m_flags = flags;
}

/*--------------------------------------------------------------------------*/

SceneInstanceContainer& CScene::GetSceneInstanceContainer(SceneInstance* scene)
{
	for (auto iter = this->m_sceneInstances.begin(); iter != this->m_sceneInstances.end(); ++iter)
	{
		if (iter->m_sceneInstance == scene)
		{
			return *iter;
		}
	}

	AssertLogC(true, "Unknown scene!");
	return this->m_sceneInstances.front();
}

/*--------------------------------------------------------------------------*/

void CScene::SetFreeCamera(PerspectiveCamera* camera)
{
	this->m_freeCamera = camera;
}

/*--------------------------------------------------------------------------*/

void CScene::UpdateFreeCamera()
{
	if (!this->m_freeCamera)
	{
		return;
	}

	float deltaTime =  g_Sdl->GetDeltaTime();

	PerspectiveCamera* pc = this->m_freeCamera;

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::UseMouse)
	{
		pc->RotateX((float) - g_Mouse->GetMovement().y);
		pc->RotateY((float) g_Mouse->GetMovement().x);
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::RotateForward)
	{
		pc->RotateX(-deltaTime * this->var_fcamera_rotation_speed->GetFloat());
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::RotateBackward)
	{
		pc->RotateX(deltaTime * this->var_fcamera_rotation_speed->GetFloat());
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::RotateUp)
	{
		pc->RotateY(-deltaTime * this->var_fcamera_rotation_speed->GetFloat());
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::RotateDown)
	{
		pc->RotateY(deltaTime * this->var_fcamera_rotation_speed->GetFloat());
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::RotateLeft)
	{
		pc->RotateZ(-deltaTime * this->var_fcamera_rotation_speed->GetFloat());
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::RotateRight)
	{
		pc->RotateZ(deltaTime * this->var_fcamera_rotation_speed->GetFloat());
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::MoveForward)
	{
		pc->SetPosition(pc->GetPosition() - pc->GetViewDirection() * deltaTime * this->var_fcamera_movement_speed->GetFloat());
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::MoveBackward)
	{
		pc->SetPosition(pc->GetPosition() + pc->GetViewDirection() * deltaTime * this->var_fcamera_movement_speed->GetFloat());
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::MoveLeft)
	{
		pc->SetPosition(pc->GetPosition() - pc->GetStrafeDirection() * deltaTime * this->var_fcamera_movement_speed->GetFloat());
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::MoveRight)
	{
		pc->SetPosition(pc->GetPosition() + pc->GetStrafeDirection() * deltaTime * this->var_fcamera_movement_speed->GetFloat());
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::MoveUp)
	{
		pc->SetPosition(pc->GetPosition() + pc->GetJumpDirection() * deltaTime * this->var_fcamera_movement_speed->GetFloat());
	}

	if (this->m_freeCameraInput & EFreeCameraMovementFlag::MoveDown)
	{
		pc->SetPosition(pc->GetPosition() - pc->GetJumpDirection() * deltaTime * this->var_fcamera_movement_speed->GetFloat());
	}
}

/*--------------------------------------------------------------------------*/

void CScene::OnKeyboardAction()
{
	this->m_freeCameraInput = 0;
	//this->key_freeCam = g_Keyboard->GetKeyState(EKey::k_lalt);

	if (g_Keyboard->GetKeyState(EKey::k_minus))
	{
		this->var_fcamera_movement_speed->Set(this->var_fcamera_movement_speed->GetFloat() * 0.9f);
		//g_Keyboard->SetKeyState(EKey::k_minus, false);
	}

	if (g_Keyboard->GetKeyState(EKey::k_equals))
	{
		this->var_fcamera_movement_speed->Set(this->var_fcamera_movement_speed->GetFloat() / 0.9f);
		//g_Keyboard->SetKeyState(EKey::k_equals, false);
	}

	if (g_Keyboard->GetKeyState(EKey::k_p))
	{
		this->m_freeCamera->SetFieldOfView(this->m_freeCamera->GetFieldOfView() + 1.0f);
		//g_Keyboard->SetKeyState(EKey::k_p, false);
	}

	if (g_Keyboard->GetKeyState(EKey::k_o))
	{
		this->m_freeCamera->SetFieldOfView(this->m_freeCamera->GetFieldOfView() - 1.0f);
		//g_Keyboard->SetKeyState(EKey::k_o, false);
	}

	if (g_Keyboard->GetKeyState(EKey::k_lalt))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::UseMouse;
	}

	if (g_Keyboard->GetKeyState(EKey::k_up))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::RotateForward;
	}

	if (g_Keyboard->GetKeyState(EKey::k_down))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::RotateBackward;
	}

	if (g_Keyboard->GetKeyState(EKey::k_q))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::RotateLeft;
	}

	if (g_Keyboard->GetKeyState(EKey::k_e))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::RotateRight;
	}

	if (g_Keyboard->GetKeyState(EKey::k_left))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::RotateUp;
	}

	if (g_Keyboard->GetKeyState(EKey::k_right))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::RotateDown;
	}

	if (g_Keyboard->GetKeyState(EKey::k_w))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::MoveForward;
	}

	if (g_Keyboard->GetKeyState(EKey::k_s))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::MoveBackward;
	}

	if (g_Keyboard->GetKeyState(EKey::k_a))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::MoveLeft;
	}

	if (g_Keyboard->GetKeyState(EKey::k_d))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::MoveRight;
	}

	if (g_Keyboard->GetKeyState(EKey::k_r))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::MoveUp;
	}

	if (g_Keyboard->GetKeyState(EKey::k_f))
	{
		this->m_freeCameraInput |= EFreeCameraMovementFlag::MoveDown;
	}
}

/*--------------------------------------------------------------------------*/

PerspectiveCamera* CScene::GetFreeCamera() const
{
	return this->m_freeCamera;
}

/*--------------------------------------------------------------------------*/

void sh::setfcampos(float x, float y, float z)
{
	PerspectiveCamera* fcam = g_Scene->GetFreeCamera();

	if (fcam)
	{
		fcam->SetPosition(glm::vec3(x, y, z));
	}
}

/*--------------------------------------------------------------------------*/

void sh::setfcamrot(float x, float y, float z)
{
	PerspectiveCamera* fcam = g_Scene->GetFreeCamera();

	if (fcam)
	{
		fcam->ZeroRotate();
		fcam->SetRotation(glm::vec3(x, y, z));
	}
}

/*--------------------------------------------------------------------------*/

const glm::vec3& sh::getfcampos()
{
	if (g_Scene->GetFreeCamera())
	{
		return g_Scene->GetFreeCamera()->GetPosition();
	}
	else
	{
		return glm::vec3(-1.0f, -1.0f, -1.0f);
	}
}

/*--------------------------------------------------------------------------*/

void sh::fcamdebug()
{
	PerspectiveCamera* fc = g_Scene->GetFreeCamera();

	if (fc)
	{
		std::cout << "#r----------------------------------------------------------------------------\n";
		std::cout << "#gPos: " << fc->GetPosition() << "\n";
		std::cout << "#gView: " << fc->GetViewMatrix() << "\n";
		std::cout << "#gProj: " << fc->GetProjectionMatrix() << "\n";
		// 	std::cout << "#gNode: " << ao->GetName() << "\n";
		// 	std::cout << "#gNodePos: " << ao->GetPosition() << "\n";
		// 	std::cout << "#gNodeRad: " << ao->GetRadius() << "\n";
		std::cout << "#gFar: " << fc->GetFarDistance() << "\n";
		std::cout << "#gNear: " << fc->GetNearDistance() << "\n";
		std::cout << "#r----------------------------------------------------------------------------\n";
	}
	else
	{
		std::cout << "#rNo free camera set!\n";
	}
}

/*--------------------------------------------------------------------------*/
